#!/usr/bin/env python3
# coding: utf-8
# vim: fdm=marker fmr={{{,}}} cms=#%s#

import random
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA

class App:
    def main(self):
        self.__run()

    def __run(self):
        __split="======================================================="

        try:
            print(__split)

            self.__test_transfer()
            print(__split)

            self.__test_random()
            print(__split)

            self.__test_base64()
            print(__split)

            self.__test_digest()
            print(__split)

            self.__test_aes()
            print(__split)

            self.__test_rsa()
            print(__split)
        except Exception as e:
            print("fail: %s" % e)
            return

    __plain="登快阁 黄庭坚（宋）"+"------------------"+"痴儿了却公家事，快阁东西倚晚晴。"+"落木千山天远大，澄江一道月分明。"+"朱弦已为佳人绝，青眼聊因美酒横。"+"万里归船弄长笛，此心吾与白鸥盟。"

    def __test_transfer(self):
#{{{#
        print("Transfer")
        print()

        plain=self.__plain;
        bs=plain.encode("UTF-8")
        s=self.__expand(bs)
        bs2=bytes.fromhex(s)
        plain2=bs2.decode("UTF-8")

        print("plain:       (%s)" % plain)
        print("bytestring:  (%s)" % bs)
        print("expand:      (%s)" % s)
        print("bytestring2: (%s)" % bs2)
        print("plain2:      (%s)" % plain2)
#}}}#

    def __test_random(self):
#{{{#
        print("Random")
        print()

        for i in range(10):
            print("[0,20): %d" % random.randrange(20))
#            print("[0,20): %d" % random.randint(0, 19))

#        items=[1,2,3,4,5,6,7]
        items=[ i for i in range(1, 20) if i % 2 == 0 ]
        print("items:   "+str(items))
        random.shuffle(items)
        print("shuffle: "+str(items))
        print("sample:  "+str(random.sample(items, 3)))
        print("choice:  "+str(random.choice(items)))
#}}}#

    def __test_base64(self):
#{{{#
        print("Base64")
        print()

        plain=self.__plain
        enc_b=base64.b64encode(plain.encode("UTF-8"))
        dec_b=base64.b64decode(enc_b)
        print("plain:   (%s)" % plain)
        print("enc_b:   (%d) (%s)" %
                (len(enc_b), enc_b.decode("UTF-8")))
        print("dec_b:   (%s)" % dec_b.decode("UTF-8"))
#}}}#

    def __test_digest(self):
#{{{#
        print("Digest")
        print()

        plain=self.__plain

        md=hashlib.md5()
        md.update(plain.encode("UTF-8"))
        md5=md.digest()

        md=hashlib.sha1()
        md.update(plain.encode("UTF-8"))
        sha1=md.digest()

        md=hashlib.sha256()
        md.update(plain.encode("UTF-8"))
        sha256=md.digest()

        md=hashlib.sha512()
        md.update(plain.encode("UTF-8"))
        sha512=md.digest()

        print("plain:   (%s)" % plain)
        print("md5:     (%s)" % self.__expand(md5))
        print("sha1:    (%s)" % self.__expand(sha1))
        print("sha256:  (%s)" % self.__expand(sha256))
        print("sha512:  (%s)" % self.__expand(sha512))
#}}}#

    def __test_aes(self):
#{{{#
        print("AES")
        print()

        BS=AES.block_size
        pad=lambda b : b + bytes([ (BS - len(b) % BS) ] * (BS - len(b) % BS))
        unpad=lambda b : b[0:-b[-1]]
        expand=lambda bs : "".join([ "%02X" % ( b & 0xff ) for b in bs ])

        plain=self.__plain
        key="1234567890123456".encode("UTF-8")

        cipher=AES.new(key, AES.MODE_ECB)
        plain_b=plain.encode("UTF-8")
#        print(plain_b)
#        print(pad(plain_b))
        enc_b=cipher.encrypt(pad(plain_b))
        dec_b=cipher.decrypt(enc_b)
#        print(dec_b)
        dec=unpad(dec_b).decode("UTF-8")

        print("plain:   (%s)" % plain)
        print("enc:     (%s)" % expand(enc_b))
#        print("enc:     (%s)" % self.__expand(enc_b))
        print("dec:     (%s)" % dec)
#}}}#

    def __test_rsa(self):
#{{{#
        print("RSA")
        print()

        expand=lambda bs : "".join([ "%02X" % ( b & 0xff ) for b in bs ])

        plain=self.__plain

        key=self.__generate_rsa_key()

        cipher_enc=PKCS1_v1_5.new(key.publickey()) 
        enc_b=cipher_enc.encrypt(plain.encode("UTF-8"))

        cipher_dec=PKCS1_v1_5.new(key)
        sentinel=Random.new().read(50)
        dec_b=cipher_dec.decrypt(enc_b, sentinel)
        dec=dec_b.decode("UTF-8")

        print("plain:   (%s)" % plain)
        print("enc:     (%s)" % expand(enc_b))
        print("dec:     (%s)" % dec)
#}}}#

    def __generate_rsa_key(self):
#{{{#
        key=RSA.generate(2048)

        f=open("public", "w")
        f.write(key.publickey().exportKey("PEM", pkcs=1).decode("UTF-8"))
        f.close()

        f=open("private", "w")
        f.write(key.exportKey("PEM", pkcs=1).decode("UTF-8"))
        f.close()

        return key
#}}}#

    def __expand(self, bs):
#{{{#
        s=[]
        for b in bs:
            s+="%02X" % (b & 0xff)
        return "".join(s)
#}}}#

if __name__ == "__main__":
    App().main()
